import numpy as np
import os
import pandas as pd
from sklearn.metrics import recall_score, precision_score
import matplotlib.pyplot as plt

VER = 'v1.0'
ALPHA = 1e-2
ITERS = 2000
BASE_DIR, FILE_NAME = os.path.split(__file__)
path = '../../large_data/ML1/hand_writing/imgX.txt'
X_PATH = os.path.join(BASE_DIR, path)
Y_PATH = os.path.join(BASE_DIR, path, '..', 'labely.txt')
SAVE_DIR = os.path.join(BASE_DIR, '_save', FILE_NAME, VER)
LOG_DIR = os.path.join(BASE_DIR, '_log', FILE_NAME, VER)

x = np.loadtxt(X_PATH, delimiter=',').astype(np.float64)
y = np.loadtxt(Y_PATH).astype(np.int64).reshape(-1, 1)
y = np.int64(y == 5)
print('x', x.shape, x.dtype)
print('y', y.shape, y.dtype)

y_pd = pd.Series(y.reshape(-1))
print('y value counts:')
print(y_pd.value_counts())

M, N = x.shape
w = np.random.randn(N, 1)
b = 0.


def model(x, w, b):
    return np.dot(x, w) + b


def sigmoid(z):
    return 1. / (1. + np.e ** (- z))


def j(a, y):
    m = len(a)
    cost = (np.dot(y.T, np.log(a)) + np.dot(1 - y.T, np.log(1 - a)))[0][0] / m
    return - cost


def dz(a, y):
    dzv = a - y
    return dzv


def dw(a, y, x):
    m = len(a)
    dzv = dz(a, y)
    dwv = np.dot(x.T, dzv) / m
    return dwv


def db(a, y):
    m = len(a)
    dbv = np.dot(np.ones((1, m), dtype=np.float64), dz(a, y)) / m
    return dbv


def accuracy(y, a):
    acc = np.float64(
        np.equal(
            a > 0.5,
            y > 0.5
        )
    ).mean()
    return acc


j_his, acc_his, precision_his, recall_his = [], [], [], []
group = int(np.ceil(ITERS / 20))
for i in range(ITERS):
    z = model(x, w, b)
    a = sigmoid(z)
    cost = j(a, y)
    j_his.append(cost)
    if i == ITERS - 1 or i % group == 0:
        acc = accuracy(y, a)
        precision = precision_score(y > 0.5, a > 0.5)
        recall = recall_score(y > 0.5, a > 0.5)
        print(f'#{i + 1}: cost = {cost}, acc = {acc}, precision = {precision}, recall = {recall}')
        acc_his.append(acc)
        precision_his.append(precision)
        recall_his.append(recall)
    dwv = dw(a, y, x)
    dbv = db(a, y)
    w -= ALPHA * dwv
    b -= ALPHA * dbv

spr = 1
spc = 2
spn = 0
plt.figure(figsize=(12, 6))
spn += 1
plt.subplot(spr, spc, spn)
plt.title('Cost')
plt.plot(j_his)
plt.grid()
spn += 1
plt.subplot(spr, spc, spn)
plt.title('Ratio')
plt.plot(acc_his, label='accuracy')
plt.plot(precision_his, label='precision')
plt.plot(recall_his, label='recall')
plt.grid()
plt.legend()
plt.show()
